package cn.wzg.client.Client.impl;

import cn.wzg.Singleton.LocalTempSingleton;
import cn.wzg.ZKService.LoadBalance;
import cn.wzg.ZKService.LoadBalanceimpl.RandomLoadBalance;
import cn.wzg.ZKService.ServiceRegister;
import cn.wzg.ZKService.ZKpojo.ZkServiceRegister;
import cn.wzg.client.Client.Init.NettyClientInitializer;
import cn.wzg.client.Client.RPCClient;
import cn.wzg.entity.Request;
import cn.wzg.entity.Response;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.Channel;
import io.netty.channel.ChannelFuture;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.util.AttributeKey;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

public class NettyRPCClient implements RPCClient {
    private static final Bootstrap bootstrap;
    private static final EventLoopGroup eventLoopGroup;
    private String host;
    private int port;

    private LoadBalance loadBalance;
    private ServiceRegister serviceRegister;
    public NettyRPCClient() {
         this.serviceRegister=new ZkServiceRegister();
         this.loadBalance=new RandomLoadBalance();
    }
    static {
        eventLoopGroup = new NioEventLoopGroup();
        bootstrap = new Bootstrap();
        bootstrap.group(eventLoopGroup).channel(NioSocketChannel.class).
                handler(new NettyClientInitializer());
    }
    @Override
    public Response sendRequest(Request request) throws IOException, ClassNotFoundException, InterruptedException {
        //在zk里获得request的接口的host和port
        InetSocketAddress inetSocketAddress = getServiceAddr(request);
        host=inetSocketAddress.getHostName();
        port=inetSocketAddress.getPort();
        ChannelFuture channelFuture  = bootstrap.connect(host, port).sync();
        System.out.println("netty启动成功");
        Channel channel = channelFuture.channel();
        // 发送数据
        channel.writeAndFlush(request);
        channel.closeFuture().sync();
        // 阻塞的获得结果，通过给channel设计别名，获取特定名字下的channel中的内容（这个在hanlder中设置）
        // AttributeKey是，线程隔离的，不会由线程安全问题。
        // 实际上不应通过阻塞，可通过回调函数
        AttributeKey<Response> key = AttributeKey.valueOf("Response");
        Response response = channel.attr(key).get();
        System.out.println(response);
        return response;
    }

    //todo:注意负载均衡策略，修改适应类型,修改负载均衡为inetsocketaddr
    public InetSocketAddress getServiceAddr(Request request){
        //从本地缓存获得
        ConcurrentHashMap<String, List<InetSocketAddress>> localtemp = LocalTempSingleton.getLocaltemp();
        List<InetSocketAddress> addresses = localtemp.get(request.getInterfaceName());
        //判断addr是否合法
        if(addresses==null || addresses.isEmpty()){
            //从zk获得服务地址
            System.out.println("缓存服务没有"+request.getInterfaceName()+"服务");
            List<InetSocketAddress> inetSocketAddress = serviceRegister.serviceDiscovery(request.getInterfaceName());
            //更新本地缓存
            System.out.println("开始更新缓存");
            localtemp.put(request.getInterfaceName(),inetSocketAddress);
            System.out.println("缓存更新成功，服务"+request.getInterfaceName()+"更新到本地缓存");
            //返回负载均衡策略的结果
            System.out.println("从zk返回结果");
            return loadBalance.balance(inetSocketAddress);
        }
        System.out.println("使用缓存，缓存服务发现");
        return loadBalance.balance(addresses);
    }
}
